package com.finance.cooperate.feature.service.impl;

import com.alibaba.fastjson.JSON;
import com.finance.cooperate.common.annotation.RetryDot;
import com.finance.cooperate.common.constant.Constant;
import com.finance.cooperate.common.properties.ApiProperties;
import com.finance.cooperate.common.properties.CooperateProperties;
import com.finance.cooperate.common.utils.*;
import com.finance.cooperate.dao.entity.primary.App;
import com.finance.cooperate.dao.entity.primary.Sms;
import com.finance.cooperate.dao.local.cache.SmsCategoryCache;
import com.finance.cooperate.dao.local.scope.CooperateRequestScope;
import com.finance.cooperate.dao.repository.primary.AppRepository;
import com.finance.cooperate.dao.repository.primary.SmsRepository;
import com.finance.cooperate.feature.core.feature.smsphone.SmsOppositesV1;
import com.finance.cooperate.feature.core.feature.smswords.SmsWordsV1;
import com.finance.cooperate.feature.dto.CompositeSmsCountDTO;
import com.finance.cooperate.feature.service.SmsService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;

@Service
@Slf4j
public class SmsServiceImpl implements SmsService {

    @Autowired
    private SmsRepository smsRepository;

    @Autowired
    private CooperateRequestScope requestScope;

    @Autowired
    private CooperateProperties cooperateProperties;


    @Override
    public CompositeSmsCountDTO getCompositeSmsCount(String uid) {

        CompositeSmsCountDTO dto = new CompositeSmsCountDTO();

        Long loanNum = requestScope.getLoanNum();
        String packageName = requestScope.getPackageName();
        String deviceId = requestScope.getDeviceId();

        // 跑特征取新数
        Date lastSubmitTime = requestScope.getLastSubmitTime();
        // 【老数据复贷没有上报数据，所以不能使用最新近件时间，应该用首次贷款时间】
//        Date lastSubmitTime = requestScope.getFirstLastSubmitTime();

        List<Sms> smsList = new ArrayList<>();

        // 首贷取数逻辑和复贷不一样
        if (loanNum > 1) {

            Date lastTime = smsRepository.getSmsLastTime(deviceId, packageName, lastSubmitTime);

            smsList = smsRepository.getSms3Month(deviceId, packageName, lastTime);

        } else {
            smsList = smsRepository.getFirstSms3Month(deviceId, packageName, lastSubmitTime);
        }

        dto.setHaveSms(0);

        if (smsList.size() > 0) {

            dto.setHaveSms(1);

            dto = this.basicStatistics(dto, smsList, lastSubmitTime);

            dto = this.processingWords01(dto, smsList, lastSubmitTime);

            Map<String, Long> phoneTimeMap = this.getOppoPhoneTimeMap(smsList, SmsOppositesV1.phone, lastSubmitTime);
            dto.setOppositesV1(phoneTimeMap);

            Map<String, Integer> loanMap = this.loanStatisticsV1(smsList, lastSubmitTime);
            dto.setLoanInfoV1(loanMap);

            try {

                // 拿代理对象
                SmsServiceImpl proxy = (SmsServiceImpl) AopContext.currentProxy();

                Double fastText01Score = proxy.fastText01(dto, smsList, lastSubmitTime);
                dto.setFastTextScore01(fastText01Score);

                Double blistm01Score = proxy.blistm01(dto, smsList, lastSubmitTime);
                dto.setBlistm01(blistm01Score);

                // 短信时间序列
                Double smsScore01 = proxy.cnnScore01(smsList, lastSubmitTime);
                dto.setSmsScore01(smsScore01);

            } catch (Exception e) {
                log.error("请求 python 服务出现异常:" + uid, e);
            }

        }

        return dto;
    }



    /**
     * @Author shenanxin
     * @Description 时间序列模型V1
     * @Date 11:01 2023/8/31
     * @Param [smsList, lastSubmitTime]
     * @return java.lang.Double
     **/
    @RetryDot
    public Double cnnScore01(List<Sms> smsList, Date lastSubmitTime) throws Exception {

        Double cnnScore = null;

        List<Map<String, String>> smsDictList = new ArrayList<>();

        for (Sms sms: smsList) {

            String smsTimeStr = sms.getSmsTime();

            if (!StringUtil.isEmptyOrNvl(smsTimeStr)) {

                Date smsTime = DateUtils.toDate(smsTimeStr);
                Integer diffDays = DateUtils.getTimeDiffDays(smsTime, lastSubmitTime);


                // 只取最近90天 + 内容不为空 + 为接收类型
                if (diffDays >= 0
                        && diffDays <= 3 * 30
                        &&!StringUtil.isEmptyOrNvl(sms.getContent())
                        &&!StringUtil.isEmptyOrNvl(sms.getType())
                        && sms.getType().equals(1)

                ) {

                    Map<String, String> mas = new HashMap<>();
                    mas.put("content", sms.getContent());
                    mas.put("oppositePhone", sms.getOppositePhone());
                    mas.put("smsTime", sms.getSmsTime());
                    mas.put("type", String.valueOf(sms.getType()));

                    smsDictList.add(mas);

                }
            }
        }

        if (smsDictList.size() > 0) {

            ApiProperties api = cooperateProperties.getApi();

            // 封装参数
            Map<String, Object> params = new HashMap<String, Object>();
            params.put("submit_time", DateUtils.toStr(lastSubmitTime));
            params.put("f_sms_data", JSON.toJSONString(smsDictList));

            String reqResult = HttpClientUtil.doPostJson(api.getSubModelUrl() + api.getSms01().getUrl(), JSON.toJSONString(params));

            if (!StringUtil.isEmptyOrNvl(reqResult)) {

                cnnScore = Double.parseDouble(reqResult);

            }

        }

        return cnnScore;

    }




    /**
    * @Author shenanxin
    * @Description 包含贷款信息短信分析
    * @Date 14:14 2023/9/21
    * @Param [smsList, lastSubmitTime]
    * @return java.util.Map<java.lang.String,java.lang.Integer>
    **/
    private Map<String, Integer> loanStatisticsV1 (List<Sms> smsList, Date lastSubmitTime) {

        Map<String, Integer> loanInfo = new HashMap<>();

        Map<String, Object> loanInfoTemp = new HashMap<>();


        boolean hasTala = true;
        boolean hasStori = true;
        boolean hasSuperapoyo = true;


        // 近一周逾期短信提醒个数
        Set<String> overdueOpposite1W = new HashSet<>();

        for (Sms sms : smsList) {

            String smsTimeStr = sms.getSmsTime();

            if (!StringUtil.isEmptyOrNvl(smsTimeStr)) {
                Date smsTime = DateUtils.toDate(smsTimeStr);

                Integer diffDays = DateUtils.getTimeDiffDays(smsTime, lastSubmitTime);

                if (diffDays >= 0 && diffDays <= 3 * 30) {

                    Integer type = sms.getType();
                    String content = sms.getContent();

                    if (type != null && type.equals(1) && !StringUtil.isEmptyOrNvl(content)) {

                        boolean isOverdue = SmsCategoryCache.isSmsOverdueV1(content);
                        boolean isLoan = SmsCategoryCache.isSmsLoanV1(content);

                        boolean isPay = SmsCategoryCache.isSmsPayV1(content);
                        boolean isIncome = SmsCategoryCache.isSmsIncomeV1(content);

                        if (diffDays < 7 && isOverdue) {
                            overdueOpposite1W.add(sms.getOppositePhone());
                        }

                        // 判断是否包含天数提醒
                        boolean haveDias = content.toLowerCase().contains(" dias");


                        boolean finalHasTala = hasTala;
                        boolean finalHasStori = hasStori;
                        boolean finalHasSuperapoyo = hasSuperapoyo;
                        SmsCategoryCache.getTimeSplitsV1().forEach((k, v) -> {

                            if (diffDays <= v) {

                                if (isOverdue) {
                                    Integer overdue = loanInfo.getOrDefault("loan_overdue_" + k, 0);
                                    loanInfo.put("loan_overdue_" + k, ++ overdue);

                                    // 获取金额
                                    Double amount = AmountExtractV1Utils.extractAmount(content);

                                    // 计算：最大金额，最小金额，平均金额，总金额，出现金额次数
                                    if (amount != null && amount > 0) {

                                        Integer amountMax = loanInfo.getOrDefault("loan_overdue_amount_max_" + k, 0);
                                        if (amount > amountMax) {
                                            loanInfo.put("loan_overdue_amount_max_" + k, amount.intValue());
                                        }

                                        Integer amountMin = loanInfo.getOrDefault("loan_overdue_amount_min_" + k, Integer.MAX_VALUE);
                                        if (amount < amountMin) {
                                            loanInfo.put("loan_overdue_amount_min_" + k, amount.intValue());
                                        }

                                        Integer amountAll = loanInfo.getOrDefault("loan_overdue_amount_all_" + k, 0);
                                        amountAll += amount.intValue();
                                        loanInfo.put("loan_overdue_amount_all_" + k, amountAll.intValue());

                                        Integer amountCount = loanInfo.getOrDefault("loan_overdue_amount_count_" + k, 0);
                                        loanInfo.put("loan_overdue_amount_count_" + k, ++ amountCount);

                                    }


                                }

                                if (isLoan) {
                                    Integer loan = loanInfo.getOrDefault("loan_loan_" + k, 0);
                                    loanInfo.put("loan_loan_" + k, ++ loan);

                                    // 获取金额
                                    Double amount = AmountExtractV1Utils.extractAmount(content);

                                    // 计算：最大金额，最小金额，平均金额，总金额，出现金额次数
                                    if (amount != null && amount > 0) {

                                        Integer amountMax = loanInfo.getOrDefault("loan_loan_amount_max_" + k, 0);
                                        if (amount > amountMax) {
                                            loanInfo.put("loan_loan_amount_max_" + k, amount.intValue());
                                        }

                                        Integer amountMin = loanInfo.getOrDefault("loan_loan_amount_min_" + k, Integer.MAX_VALUE);
                                        if (amount < amountMin) {
                                            loanInfo.put("loan_loan_amount_min_" + k, amount.intValue());
                                        }

                                        Integer amountAll = loanInfo.getOrDefault("loan_loan_amount_all_" + k, 0);
                                        amountAll += amount.intValue();
                                        loanInfo.put("loan_loan_amount_all_" + k, amountAll.intValue());

                                        Integer amountCount = loanInfo.getOrDefault("loan_loan_amount_count_" + k, 0);
                                        loanInfo.put("loan_loan_amount_count_" + k, ++ amountCount);


                                    }
                                }

                                if (isPay) {
                                    Integer pay = loanInfo.getOrDefault("loan_pay_" + k, 0);
                                    loanInfo.put("loan_pay_" + k, ++ pay);
                                }

                                if (isIncome) {
                                    Integer income = loanInfo.getOrDefault("loan_income_" + k, 0);
                                    loanInfo.put("loan_income_" + k, ++ income);
                                }



                                Integer common = loanInfo.getOrDefault("loan_common_" + k, 0);
                                loanInfo.put("loan_common_" + k, ++ common);


                                if (finalHasTala || finalHasStori || finalHasSuperapoyo) {

                                    Set<String> tokens = SmsPreprocessV1Utils.getTokens(content);

                                    if (tokens.contains("tala") || tokens.contains("stori") || tokens.contains("superapoyo")) {
                                        if (isOverdue) {
                                            Integer overdue = loanInfo.getOrDefault("loan_overdue_top_competition_" + k, 0);
                                            loanInfo.put("loan_overdue_top_competition_" + k, ++ overdue);
                                        }

                                        if (isLoan) {
                                            Integer loan = loanInfo.getOrDefault("loan_loan_top_competition_" + k, 0);
                                            loanInfo.put("loan_loan_top_competition_" + k, ++ loan);
                                        }
                                    }


                                    if (finalHasTala && tokens.contains("tala")) {
                                        if (isOverdue) {
                                            Integer overdue = loanInfo.getOrDefault("loan_overdue_tala_" + k, 0);
                                            loanInfo.put("loan_overdue_tala_" + k, ++ overdue);
                                        }

                                        if (isLoan) {
                                            Integer loan = loanInfo.getOrDefault("loan_loan_tala_" + k, 0);
                                            loanInfo.put("loan_loan_tala_" + k, ++ loan);
                                        }
                                    }


                                    if (finalHasStori && tokens.contains("stori")) {
                                        if (isOverdue) {
                                            Integer overdue = loanInfo.getOrDefault("loan_overdue_stori_" + k, 0);
                                            loanInfo.put("loan_overdue_stori_" + k, ++ overdue);
                                        }

                                        if (isLoan) {
                                            Integer loan = loanInfo.getOrDefault("loan_loan_stori_" + k, 0);
                                            loanInfo.put("loan_loan_stori_" + k, ++ loan);
                                        }
                                    }


                                    if (finalHasSuperapoyo && tokens.contains("superapoyo")) {
                                        if (isOverdue) {
                                            Integer overdue = loanInfo.getOrDefault("loan_overdue_superapoyo_" + k, 0);
                                            loanInfo.put("loan_overdue_superapoyo_" + k, ++ overdue);
                                        }

                                        if (isLoan) {
                                            Integer loan = loanInfo.getOrDefault("loan_loan_superapoyo_" + k, 0);
                                            loanInfo.put("loan_loan_superapoyo_" + k, ++ loan);
                                        }
                                    }
                                }


                                if (haveDias) {
                                    Integer dias = loanInfo.getOrDefault("loan_dias_" + k, 0);
                                    loanInfo.put("loan_dias_" + k, ++ dias);

                                    // 提取 dias前的数字信息
                                    Integer days = SmsRegularUtils.extractDias(content.toLowerCase());

                                    if (days != null && days > 0) {

                                        Integer daysMax = loanInfo.getOrDefault("loan_dias_max_" + k, 0);
                                        if (days > daysMax) {
                                            loanInfo.put("loan_dias_max_" + k, days);
                                        }

                                        Integer daysMin = loanInfo.getOrDefault("loan_dias_min_" + k, Integer.MAX_VALUE);
                                        if (days < daysMin) {
                                            loanInfo.put("loan_dias_min_" + k, days);
                                        }

                                        List<Integer> diasList = (ArrayList<Integer>) loanInfoTemp.getOrDefault("loan_dias_list_" + k, new ArrayList<Integer>());
                                        diasList.add(days);
                                        loanInfoTemp.put("loan_dias_list_" + k, diasList);

                                    }

                                }


                            }

                        });

                    }

                }

            }

        }


        // 一些聚合变量
        SmsCategoryCache.getTimeSplitsV1().forEach((k, v) -> {

            // 平均金额 = [amountAll - (min + max)] / (amountCount - 2)
            Integer amountOverdueAll = loanInfo.getOrDefault("loan_overdue_amount_all_" + k, null);
            Integer amountOverdueCount = loanInfo.getOrDefault("loan_overdue_amount_count_" + k, null);

            Integer amountOverdueMax = loanInfo.getOrDefault("loan_overdue_amount_max_" + k, null);
            Integer amountOverdueMin = loanInfo.getOrDefault("loan_overdue_amount_min_" + k, null);

            if (amountOverdueCount != null && amountOverdueCount > 2) {

                Integer amountOverdueAvg = (amountOverdueAll - (amountOverdueMin + amountOverdueMax)) / (amountOverdueCount - 2);

                loanInfo.put("loan_overdue_amount_avg_" + k, amountOverdueAvg);
            }



            Integer amountLoanAll = loanInfo.getOrDefault("loan_loan_amount_all_" + k, null);
            Integer amountLoanCount = loanInfo.getOrDefault("loan_loan_amount_count_" + k, null);

            Integer amountLoanMax = loanInfo.getOrDefault("loan_loan_amount_max_" + k, null);
            Integer amountLoanMin = loanInfo.getOrDefault("loan_loan_amount_min_" + k, null);

            if (amountLoanCount != null && amountLoanCount > 2) {

                Integer amountLoanAvg = (amountLoanAll - (amountLoanMin + amountLoanMax)) / (amountLoanCount - 2);

                loanInfo.put("loan_loan_amount_avg_" + k, amountLoanAvg);
            }


            List<Integer> diasList = (ArrayList<Integer>) loanInfoTemp.getOrDefault("loan_dias_list_" + k, new ArrayList<Integer>());

            if (diasList.size() > 0) {
                // (次数，平均值，中位数) * {list, set}

                loanInfo.put("loan_dias_count_" + k, diasList.size());

                Double diasAvg = diasList.stream()
                        .mapToInt(Integer::intValue)
                        .average()
                        .orElse(0);

                loanInfo.put("loan_dias_avg_" + k, diasAvg.intValue());


                Double diasMedia = DigitalUtils.medianCalculate(diasList);
                loanInfo.put("loan_dias_median_" + k, diasMedia.intValue());


                Set<Integer> diasSet = new HashSet<>(diasList);

                loanInfo.put("loan_dias_distinct_count_" + k, diasSet.size());


                Double diasDistinctAvg = diasSet.stream()
                        .mapToInt(Integer::intValue)
                        .average()
                        .orElse(0);

                loanInfo.put("loan_dias_distinct_avg_" + k, diasDistinctAvg.intValue());

                Double diasDistinctMedia = DigitalUtils.medianCalculate(new ArrayList<>(diasSet));
                loanInfo.put("loan_dias_distinct_median_" + k, diasDistinctMedia.intValue());


            }



        });


        loanInfo.put("loan_overdue_opposite_1w", overdueOpposite1W.size());

        loanInfoTemp.clear();

        return loanInfo;

    }


    @RetryDot
    public Double blistm01(CompositeSmsCountDTO dto, List<Sms> smsList, Date lastSubmitTime) throws Exception {

        Double prod = null;

        StringBuilder tokens = new StringBuilder();

        for (Sms sms: smsList) {

            String smsTimeStr = sms.getSmsTime();

            if (!StringUtil.isEmptyOrNvl(smsTimeStr)) {

                Date smsTime = DateUtils.toDate(smsTimeStr);
                Integer diffDays = DateUtils.getTimeDiffDays(smsTime, lastSubmitTime);

                // 取最 3*30 天 + 内容不为空 + 为接收类型 + 为指定发送方
                if (diffDays >= 0
                        && diffDays <= 30
                        && !StringUtil.isEmptyOrNvl(sms.getContent())
                        && !StringUtil.isEmptyOrNvl(sms.getType())
                        && sms.getType().equals(1)
                ) {

                    tokens.append(
                            SmsPreprocessV2Utils.getTokensJoin(sms.getContent())
                    ).append(" ");

                }
            }
        }

        String tokensStr = tokens.toString().trim();

        if (tokensStr.length() > 0) {

            ApiProperties api = cooperateProperties.getApi();

            String reqResult = HttpClientUtil.doPostJson(api.getSubModelUrl() + api.getBlistm01().getUrl(), JSON.toJSONString(tokensStr));

            if (!StringUtil.isEmptyOrNvl(reqResult)) {

                prod = Double.parseDouble(reqResult);

            }
        }

        return prod;

    }


    /**
    * @Author shenanxin
    * @Description 对方手机统计
    * @Date 14:06 2023/9/13
    * @Param [smsList, opppPhoneList, lastSubmitTime]
    * @return java.util.Map<java.lang.String,java.lang.Long>
    **/
    private Map<String, Long> getOppoPhoneTimeMap(List<Sms> smsList, Set<String> oppoList, Date lastSubmitTime) {

        Map<String, Long> oppoPhoneTime = new HashMap<>();

        try {

            if (smsList.size() > 0) {

                // 防止多次遍历
                Map<String, Integer> smsCountMap = new HashMap<>();
                for (Sms sms : smsList) {
                    String oppositePhone = sms.getOppositePhone();

                    if (!StringUtil.isEmptyOrNvl(oppositePhone) && oppoList.contains(oppositePhone) && sms.getType().equals(1)) {

                        String smsTimeStr = sms.getSmsTime();

                        if (!StringUtil.isEmptyOrNvl(smsTimeStr)) {

                            Date smsTime = DateUtils.toDate(smsTimeStr);

                            Integer diffDays = DateUtils.getTimeDiffDays(smsTime, lastSubmitTime);

                            SmsCategoryCache.getTimeSplitsV1().forEach((k, v) -> {
                                if (diffDays <= v) {
                                    Integer count = smsCountMap.getOrDefault("send_" + oppositePhone.replace(" ", "_") + "_" + k, 0);
                                    smsCountMap.put("send_" + oppositePhone.replace(" ", "_") + "_" + k, ++ count);
                                }
                            });
                        }
                    }
                }


                // 整体统计完毕：
                for (String opp : oppoList) {

                    opp = opp.replace(" ", "_");

                    String finalOpp = opp;
                    SmsCategoryCache.getTimeSplitsV1().forEach((k, v) -> {

                        Integer count = smsCountMap.getOrDefault("send_" + finalOpp + "_" + k, 0);

                        oppoPhoneTime.put("send_" + finalOpp + "_" + k + "_0", Constant.IS_NO_HIT);
                        oppoPhoneTime.put("send_" + finalOpp + "_" + k + "_1", Constant.IS_NO_HIT);
                        oppoPhoneTime.put("send_" + finalOpp + "_" + k + "_2", Constant.IS_NO_HIT);
                        oppoPhoneTime.put("send_" + finalOpp + "_" + k + "_3", Constant.IS_NO_HIT);
                        oppoPhoneTime.put("send_" + finalOpp + "_" + k + "_4", Constant.IS_NO_HIT);
                        oppoPhoneTime.put("send_" + finalOpp + "_" + k + "_5", Constant.IS_NO_HIT);

                        if (count > 0 && count <= 1) {
                            oppoPhoneTime.put("send_" + finalOpp + "_" + k + "_0", Constant.IS_HIT);
                        } else if (count > 1 && count <= 5) {
                            oppoPhoneTime.put("send_" + finalOpp + "_" + k + "_1", Constant.IS_HIT);
                        } else if (count > 5 && count <= 12) {
                            oppoPhoneTime.put("send_" + finalOpp + "_" + k + "_2", Constant.IS_HIT);
                        } else if (count > 12 && count <= 40) {
                            oppoPhoneTime.put("send_" + finalOpp + "_" + k + "_3", Constant.IS_HIT);
                        } else if (count > 40 && count <= 120) {
                            oppoPhoneTime.put("send_" + finalOpp + "_" + k + "_4", Constant.IS_HIT);
                        } else if (count > 120) {
                            oppoPhoneTime.put("send_" + finalOpp + "_" + k + "_5", Constant.IS_HIT);
                        }

                    });

                }

            }

        } catch (Exception e) {
            log.error(e.toString(), e);
        }


        return oppoPhoneTime;
    }




    @RetryDot
    public Double fastText01(CompositeSmsCountDTO dto, List<Sms> smsList, Date lastSubmitTime) throws Exception{

        Double prod = null;

        StringBuilder tokens = new StringBuilder();

        for (Sms sms: smsList) {

            String smsTimeStr = sms.getSmsTime();

            if (!StringUtil.isEmptyOrNvl(smsTimeStr)) {

                Date smsTime = DateUtils.toDate(smsTimeStr);
                Integer diffDays = DateUtils.getTimeDiffDays(smsTime, lastSubmitTime);

                // 只取最15天 + 内容不为空 + 为接收类型
                if (diffDays >= 0
                        && diffDays <= 15
                        && !StringUtil.isEmptyOrNvl(sms.getContent())
                        && !StringUtil.isEmptyOrNvl(sms.getType())
                        && sms.getType().equals(1)

                ) {

                    tokens.append(
                            SmsPreprocessV1Utils.getTokensJoin(sms.getContent())
                    ).append(" ");

                }

            }
        }

        String tokensStr = tokens.toString().trim();

        if (tokensStr.length() > 0) {
            ApiProperties api = cooperateProperties.getApi();

            String reqResult = HttpClientUtil.doPostJson(api.getSubModelUrl() + api.getFastText01().getUrl(), JSON.toJSONString(tokensStr));

            if (!StringUtil.isEmptyOrNvl(reqResult)) {
                prod = Double.parseDouble(reqResult);
            }

        }


        return prod;

    }


    /**
     * @Author shen
     * @Description 处理关键词特征
     * @Date 16:12 2023/8/2
     * @Param [dto, smsList, lastSubmitTime]
     * @return com.finance.cooperate.feature.dto.CompositeSmsCountDTO
     **/
    public CompositeSmsCountDTO processingWords01(CompositeSmsCountDTO dto, List<Sms> smsList, Date lastSubmitTime){

        Map<String, Integer> wordsMap = new HashMap<>();

        // 先获取所有的关键词
        Set<String> tokens = new HashSet<>();

        for (Sms sms: smsList) {

            String smsTimeStr = sms.getSmsTime();

            if (!StringUtil.isEmptyOrNvl(smsTimeStr)) {

                Date smsTime = DateUtils.toDate(smsTimeStr);
                Integer diffDays = DateUtils.getTimeDiffDays(smsTime, lastSubmitTime);

                // 只取最15天 + 内容不为空 + 为接收类型
                if (diffDays >= 0
                        && diffDays <= 15
                        && !StringUtil.isEmptyOrNvl(sms.getContent())
                        && !StringUtil.isEmptyOrNvl(sms.getType())
                        && sms.getType().equals(1)

                ) {
                    tokens.addAll(
                            SmsPreprocessV1Utils.getTokens(sms.getContent())
                    );
                }
            }
        }

        // 判断是否存在指定关键词
        SmsWordsV1.words.forEach(w -> {
            if (tokens.contains(w)) {
                wordsMap.put(w, 1);
            } else {
                wordsMap.put(w, 0);
            }
        });

        dto.setWords01(wordsMap);

        return dto;
    }



    /**
     * @Author shen
     * @Description 基本信息统计，统计以下几个信息
     * 1. 发送、接收短信个数
     * 2. 接收短信中，对方是手机号的比例
     * 3. 接收短信中，包含 "[], <>" 短信的个数
     * 4. 周期内同一手机号发送短信的个数：
     * 4.1 时间周期 <=：1w, 2w, 3w, 1m, 2m, 3m
     * 4.2 接收到的短信个数 >=：2, 6, 9, 12, 15, 20, 25, 30, 60
     * 5. URL 个数
     * 6. SPEI 短信类型和金额：
     * 6.1 类型：收款，转出
     * 6.2 获取额度
     *
     * @Date 16:50 2023/4/28
     * @Param [dto, smsList, lastSubmitTime]
     * @return com.finance.cooperate.feature.dto.CompositeSmsCountDTO
     **/
    private CompositeSmsCountDTO basicStatistics(CompositeSmsCountDTO dto, List<Sms> smsList, Date lastSubmitTime) {

        Map<String, Object> basicInfo = new HashMap<>();

        for (Sms sms : smsList) {

            String smsTimeStr = sms.getSmsTime();

            if (!StringUtil.isEmptyOrNvl(smsTimeStr)) {

                Date smsTime = DateUtils.toDate(smsTimeStr);

                Integer diffDays = DateUtils.getTimeDiffDays(smsTime, lastSubmitTime);

                if(diffDays >= 0 && diffDays <= 3 * 30) {

                    Integer type = sms.getType();

                    if (type != null) {

                        if (type.equals(1)) {

                            String content = sms.getContent();
                            String oppositePhone = sms.getOppositePhone();
                            if (StringUtil.isEmptyOrNvl(content) || StringUtil.isEmptyOrNvl(oppositePhone)) {
                                continue;
                            }

                            oppositePhone = oppositePhone.replace(" ", "").toLowerCase();

                            // 判断是否包含 [], <>
                            boolean haveSquare = (content.contains("[") && content.contains("]"))
                                    || (content.contains("<") && content.contains(">"));

                            // 判断是否为手机号
                            Long phone = StringUtil.digitalLongExtraction(oppositePhone);
                            boolean isPhoneNum = (phone != null) && (phone > 1000000);

                            // 判断是否包含网址
                            boolean haveWebsite = SmsRegularUtils.containsWebsite(content);

                            // 判断是否包含 SPEI
                            boolean haveSpei = content.toLowerCase().contains(" spei");


                            // 这里需要
                            String finalOppositePhone = oppositePhone;

                            SmsCategoryCache.getTimeSplitsV1().forEach((k, v) -> {
                                if (diffDays <= v) {
                                    // 计算接收短信的个数
                                    Integer receiveCount = (Integer) basicInfo.getOrDefault("receive_count_" + k, 0);
                                    basicInfo.put("receive_count_" + k, ++ receiveCount);

                                    if (haveSquare) {
                                        Integer squareCount = (Integer) basicInfo.getOrDefault("square_count_" + k, 0);
                                        basicInfo.put("square_count_" + k, ++ squareCount);
                                    }

                                    if (isPhoneNum) {
                                        Integer phoneNumCount = (Integer) basicInfo.getOrDefault("phone_num_count_" + k, 0);
                                        basicInfo.put("phone_num_count_" + k, ++ phoneNumCount);
                                    }

                                    if (haveWebsite) {
                                        Integer websiteCount = (Integer) basicInfo.getOrDefault("website_count_" + k, 0);
                                        basicInfo.put("website_count_" + k, ++ websiteCount);
                                    }


                                    // 统计各短信发送方个数
                                    Map<String, Integer> oppositeMap = (Map<String, Integer>) basicInfo.getOrDefault("opposite_" + k, new HashMap<String, Integer>());

                                    Integer oppositeCount = oppositeMap.getOrDefault(finalOppositePhone, 0);
                                    oppositeMap.put(finalOppositePhone, ++ oppositeCount);

                                    basicInfo.put("opposite_" + k, oppositeMap);


                                    if (haveSpei) {

                                        String speiType = SmsRegularUtils.extractSpeiType(content.toLowerCase());

                                        if (speiType != null) {

                                            Integer speiAmount = SmsRegularUtils.extractSpeiAmount(content);

                                            if (speiAmount != null) {

                                                Integer speiCount = (Integer) basicInfo.getOrDefault("spei_" + speiType + "_count_" + k, 0);
                                                basicInfo.put("spei_" + speiType + "_count_" + k, ++ speiCount);


                                                Integer amount = (Integer) basicInfo.getOrDefault("spei_" + speiType + "_amount_" + k, 0);
                                                basicInfo.put("spei_" + speiType + "_amount_" + k, amount + speiAmount);

                                            }
                                        }

                                    }


                                    // 计算哪些天有接收短信
                                    Set<String> receiveDaysSet = (Set<String>) basicInfo.getOrDefault("receive_days_set_" + k, new HashSet<String>());
                                    receiveDaysSet.add(DateUtils.toStr(DateUtils.toDate(smsTimeStr), DateUtils.YYYY_MM_DD));

                                    basicInfo.put("receive_days_set_" + k, receiveDaysSet);

                                }
                            });

                            // 提取出短信日期
                            Date containDate = SmsRegularUtils.extractDate(content);

                            if (containDate != null) {

                                // smsTime - 短信包含的日期：N，30，15，7，3，1，0，-1，-3，-7，-15，-30，-N

                                // 两个日期都舍弃末尾
                                Date smsTimeP = DateUtils.toDate(DateUtils.toStr(smsTime, DateUtils.YYYY_MM_DD), DateUtils.YYYY_MM_DD);
                                Date containDateP = DateUtils.toDate(DateUtils.toStr(containDate, DateUtils.YYYY_MM_DD), DateUtils.YYYY_MM_DD);

                                Integer timeDiffDays = DateUtils.getTimeDiffDays(smsTimeP, containDateP);

                                Integer lastT = Integer.MAX_VALUE;
                                for (Integer t : Arrays.asList(30, 15, 7, 3, 1, 0, -1, -3, -7, -15, -30, Integer.MIN_VALUE)) {

                                    if (timeDiffDays < lastT && timeDiffDays >= t) {

                                        if (t.equals(Integer.MIN_VALUE)) {
                                            Integer count = (Integer) basicInfo.getOrDefault("contain_date_diff_d", 0);
                                            basicInfo.put("contain_date_diff_d" + t, ++ count);
                                        } else if (t >= 0) {
                                            Integer count = (Integer) basicInfo.getOrDefault("contain_date_diff_" + t, 0);
                                            basicInfo.put("contain_date_diff_" + t, ++ count);
                                        } else {
                                            Integer count = (Integer) basicInfo.getOrDefault("contain_date_diff_d" + Math.abs(t), 0);
                                            basicInfo.put("contain_date_diff_d" + Math.abs(t), ++ count);
                                        }

                                    }

                                    lastT = t;

                                }
                            }


                        } else if (type.equals(2)) {
                            SmsCategoryCache.getTimeSplitsV1().forEach((k, v) -> {

                                if (diffDays <= v) {

                                    // 计算发送短信的个数
                                    Integer sendCount = (Integer) basicInfo.getOrDefault("send_count_" + k, 0);

                                    basicInfo.put("send_count_" + k, ++ sendCount);


                                    // 计算哪些天有发送短信
                                    Set<String> sendDaysSet = (Set<String>) basicInfo.getOrDefault("send_days_set_" + k, new HashSet<String>());
                                    sendDaysSet.add(DateUtils.toStr(DateUtils.toDate(smsTimeStr), DateUtils.YYYY_MM_DD));

                                    basicInfo.put("send_days_set_" + k, sendDaysSet);

                                }

                            });
                        }

                    }
                }
            }
        }



        // 后续数据处理
        SmsCategoryCache.getTimeSplitsV1().forEach((k, v) -> {

            Integer receiveCount = (Integer) basicInfo.getOrDefault("receive_count_" + k, 0);

            Integer phoneNumCount = (Integer) basicInfo.getOrDefault("phone_num_count_" + k, 0);

            double phoneRate = -1d;
            if (receiveCount > 0) {
                phoneRate = (double) phoneNumCount / receiveCount;
            }
            basicInfo.put("phone_rate_" + k, phoneRate);



            // spei 数据差值
            Integer receiptAmount = (Integer) basicInfo.getOrDefault("spei_receipt_amount_" + k, 0);
            Integer paymentAmount = (Integer) basicInfo.getOrDefault("spei_payment_amount_" + k, 0);

            Integer incomeAmount = receiptAmount - paymentAmount;
            basicInfo.put("spei_income_amount_" + k, incomeAmount);



            Map<String, Integer> oppositeMap = (Map<String, Integer>) basicInfo.getOrDefault("opposite_" + k, new HashMap<String, Integer>());


            // 接收到的短信个数 >=：2, 6, 9, 12, 15, 20, 25, 30, 60
            for (Integer i : Arrays.asList(2, 6, 9, 12, 15, 20, 25, 30, 60)) {

                for (Integer value : oppositeMap.values()) {
                    if (value >= i) {

                        Integer sameOppositeCount = (Integer) basicInfo.getOrDefault("same_opposite_" + k + "_" + i, 0);
                        basicInfo.put("same_opposite_" + k + "_" + i, ++ sameOppositeCount);

                    }
                }
            }

            oppositeMap.clear();


            // 计算哪些天有接收短信
            Set<String> receiveDaysSet = (Set<String>) basicInfo.getOrDefault("receive_days_set_" + k, new HashSet<String>());
            basicInfo.put("receive_days_count_" + k, receiveDaysSet.size());
            receiveDaysSet.clear();


            // 计算哪些天有发送短信
            Set<String> sendDaysSet = (Set<String>) basicInfo.getOrDefault("send_days_set_" + k, new HashSet<String>());
            basicInfo.put("send_days_count_" + k, sendDaysSet.size());
            sendDaysSet.clear();


        });

        dto.setBasicInfoV1(basicInfo);


        return dto;
    }


}
